package com.roin.learn.springsecurity.config.security;

import com.roin.learn.springsecurity.entity.SysPermission;
import com.roin.learn.springsecurity.service.SysAdminService;
import com.roin.learn.springsecurity.service.SysPermissionService;
import com.roin.learn.springsecurity.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SysAdminService sysAdminService;

    @Autowired
    SysPermissionService sysPermissionService;

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        /**
         * 获取登录用户信息及其权限信息
         * spring会根据设置的加密方式自动对用户密码进行验证
         */
        return username -> sysAdminService.loadUserByUsername(username);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        //设置默认的加密方式
        return new BCryptPasswordEncoder();
    }

    @Bean
    public IgnoreUrlsConfig ignoreUrlsConfig() {
        return new IgnoreUrlsConfig();
    }

    /**
     * 动态权限业务实现
     * @return
     */
    @Bean
    public DynamicSecurityService dynamicSecurityService() {
        return () -> {
            //加载资源路径和资源权限对应MAP
            Map<String, ConfigAttribute> map = new ConcurrentHashMap<>(5);
            List<SysPermission> permissionList = sysPermissionService.list();
            permissionList.forEach(permission ->
                map.put(permission.getUrl(),new org.springframework.security.access.SecurityConfig(permission.getId() + ":" + permission.getName()))
            );
            return map;
        };
    }

    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
        return new DynamicAccessDecisionManager();
    }


    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicSecurityFilter dynamicSecurityFilter() {
        return new DynamicSecurityFilter();
    }

    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() {
        return new DynamicSecurityMetadataSource();
    }

    @Bean
    public RestAccessDeniedHandler restfulAccessDeniedHandler() {
        return new RestAccessDeniedHandler();
    }

    @Bean
    public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
        return new RestAuthenticationEntryPoint();
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }
    @Bean
    public JwtTokenUtil jwtTokenUtil() {
        return new JwtTokenUtil();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置用户权限获取实现
        auth.userDetailsService(userDetailsService())
                //设置密码加密方式，数据库保存的密码也应该使用相同的加密方式保存
                .passwordEncoder(passwordEncoder());
    }


//    @Autowired
//    VerifyCodeFilter verifyCodeFilter;
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //不需要保护的资源路径允许访问
        for (String url : ignoreUrlsConfig().getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        //http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();
        registry.and().authorizeRequests()//开启登录配置
                    .anyRequest().authenticated()//表示剩余的其他接口，登录之后就能访问
                //关闭跨站请求防护
                .and().csrf().disable()
                //异常处理(权限拒绝、登录失效等)
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())//权限拒绝处理逻辑
                .authenticationEntryPoint(restAuthenticationEntryPoint())//匿名用户访问无权限资源时的异常处理
                //Session管理
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                //.and().sessionManagement()
                //.maximumSessions(1)//同一账号同时登录最大用户数
                //.expiredSessionStrategy(sessionInformationExpiredStrategy())//Session失效处理逻辑
                // 自定义权限拦截器JWT过滤器
                .and()
                //将JWT过滤放到Spring Security的用户信息验证过滤器前
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
                //动态权限校验过滤器
                registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);

    }
}